using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using FernUniHagen.CompilerExtension.DataStructures;
using FernUniHagen.CompilerExtension.Attributes;
using Roslyn.Compilers.CSharp;
using FernUniHagen.CompilerExtension.Functions;
using Roslyn.Compilers.Common;

namespace FernUniHagen.CompilerExtension.Converter.Block
{
    /// <summary>
    /// TODO: Return statements need rework as they dont work with other replacements
    /// TODO: return null does not work. Question is if null is allowed?
    /// </summary>
    public class ReturnStatementConverter : BlockNodeFactory
    {
        public override List<StatementSyntax> ConvertBlockNode()
        {
            var statementsToAdd = new List<StatementSyntax>();
            var returnStatement = Utilities.CastNode<ReturnStatementSyntax>(NodeToReplaceInBlock.Statement);
            var methodDeclaration = NodeToReplaceInBlock.Statement.FirstAncestorOrSelf<MethodDeclarationSyntax>();
            GenericNameSyntax genericTypeMethod = null;
            AttributeType methodAttributeType = AttributeType.None;
            Utilities.GetMultiplicityAttribute(methodDeclaration, out methodAttributeType, out genericTypeMethod);

            var expression = (ExpressionSyntax)NodeToReplaceInBlock.ReferencedNode;
            if (expression != null)
            {
                //TODO: Throws exception when for example null is returned
                var identifier = (IdentifierNameSyntax)expression.DescendantNodesAndSelf().First(n => n is IdentifierNameSyntax);
                var returnValueDeclaration = SemanticModel.FindDecleration( identifier);
                AttributeType returnValueAttributeType = Utilities.GetMultiplicityAttribute(returnValueDeclaration);
                //Implementation of R-Return2 (return option or bare as if casted to any)
                //TODO: Refactor to a method as same logic will be used later
                if (methodAttributeType == AttributeType.Any && returnValueAttributeType != AttributeType.Any)
                {
                    var expressionVariable = VariableCreatorService.GetTempVariable();
                    var newVariable = VariableCreatorService.GetTempVariable();
                    var saveExpressionStatement = Syntax.ParseStatement(string.Format("var {0} = {1};" + Environment.NewLine, expressionVariable, expression));
                    var initListStmt = Syntax.ParseStatement(string.Format("var {0} = new {1}();" + Environment.NewLine, newVariable, genericTypeMethod.ToString()));

                    var condStmt = Syntax.ParseStatement(string.Format("if({0} != null) {1}.Add({0});" + Environment.NewLine, expressionVariable, newVariable));
                    var newIdentifier = Syntax.IdentifierName(newVariable);
                    statementsToAdd.AddRange(saveExpressionStatement, initListStmt, condStmt);
                    StatementReplacements = new StatementReplacement<CommonSyntaxNode>(NodeToReplaceInBlock.ReferencedNode, newIdentifier);
                  //  StatementReplacements.Add(new StatementReplacement(NodeToReplaceInBlock.ReferencedNode, newIdentifier));

                }
                //Part of the rule R-Return1
                else if (methodAttributeType == AttributeType.Any && returnValueAttributeType == AttributeType.Any)
                {
                    StatementSyntax returnStmt = null;
                    GenericNameSyntax genericTypeIdentifier = null;
                    Utilities.GetMultiplicityAttribute(returnValueDeclaration, out returnValueAttributeType, out genericTypeIdentifier);

                    //Apply R-Return1 if generic types are identical
                    if (genericTypeIdentifier.IsEquivalentTo(genericTypeMethod))
                    {
                        // var newStmt = Syntax.ParseExpression(string.Format("new {0}({1});", genericTypeMethod.ToString(), identifier.ToString()));
                        StatementReplacements = new StatementReplacement<CommonSyntaxNode>(NodeToReplaceInBlock.ReferencedNode, NodeToReplaceInBlock.ReferencedNode);
                      //  StatementReplacements.Add(new StatementReplacement(NodeToReplaceInBlock.ReferencedNode, NodeToReplaceInBlock.ReferencedNode));
                        //returnStmt = Syntax.ParseStatement(string.Format("return new {0}({1});" + Environment.NewLine,
                        //genericTypeMethod.ToString(), identifier.ToString()));
                    }
                    //Apply R-Return2 if generic types are not identical
                    else
                    {
                        var expressionVariable = VariableCreatorService.GetTempVariable();
                        var newVariable = VariableCreatorService.GetTempVariable();
                        var saveExpressionStatement = Syntax.ParseStatement(string.Format("var {0} = {1};" + Environment.NewLine, expressionVariable, expression));
                        var initListStmt = Syntax.ParseStatement(string.Format("var {0} = new {1}();" + Environment.NewLine, newVariable, genericTypeMethod.ToString()));

                        var condStmt = Syntax.ParseStatement(string.Format("if({0} != null) {1}.Add({0});" + Environment.NewLine, expressionVariable, newVariable));
                        returnStmt = Syntax.ParseStatement(string.Format("return {0};" + Environment.NewLine, newVariable));
                        var newIdentifier = Syntax.IdentifierName(newVariable);
                        statementsToAdd.AddRange(saveExpressionStatement, initListStmt, condStmt);
                        StatementReplacements = new StatementReplacement<CommonSyntaxNode>(NodeToReplaceInBlock.ReferencedNode, newIdentifier);
                       // StatementReplacements.Add(new StatementReplacement(NodeToReplaceInBlock.ReferencedNode, newIdentifier));
                    }

                 //   statementsToAdd.Add(returnStmt);
                }
               
            }
          
            return statementsToAdd;
        }

        
    }
}
